﻿Imports System.Text
Imports System.Net
Imports System.Xml
Imports System.IO

Namespace TwitterAPI
    Public Class Client
        Private connection As Connection

        Private Shared Function GetTwitsByHashCodeImplementation(ByVal url As String) As List(Of FeedItem)
            Dim ret As New List(Of FeedItem)
            Dim request = HttpWebRequest.Create(url)
            Using response = request.GetResponse()
                Client.FindResponseErrors(response)
                Using reader = XmlReader.Create(response.GetResponseStream())
                    If reader.ReadToDescendant("entry") Then
                        Do
                            Dim message = ParseFeedItem(reader)
                            ret.Add(message)
                        Loop While reader.ReadToFollowing("entry")
                    End If
                End Using
            End Using
            Return ret
        End Function

        Public Shared Function GetTwitsByHashCode(ByVal hashCode As String) As List(Of FeedItem)
            Return GetTwitsByHashCodeImplementation(String.Format("http://search.twitter.com/search.atom?tag={0}", Uri.EscapeDataString(hashCode)))
        End Function

        Public Shared Function GetTwitsByHashCode(ByVal hashCode As String, ByVal since As DateTime) As List(Of FeedItem)
            Return GetTwitsByHashCodeImplementation(String.Format("http://search.twitter.com/search.atom?tag={0}&since={1}", Uri.EscapeDataString(hashCode), Uri.EscapeUriString(since.ToString(("yyyy-MM-dd")))))
        End Function

        Public Shared Function GetTwitsByHashCode(ByVal hashCode As String, ByVal since As Long) As List(Of FeedItem)
            Return GetTwitsByHashCodeImplementation(String.Format("http://search.twitter.com/search.atom?tag={0}&since_id={1}", Uri.EscapeDataString(hashCode), since))
        End Function

        Private Function GetRepliesImplementation(ByVal url As String) As List(Of FeedItem)
            Dim ret As New List(Of FeedItem)
            Dim request = connection.CreateRequest(url)
            Using response = request.GetResponse()
                FindResponseErrors(response)
                Using reader = XmlReader.Create(response.GetResponseStream())
                    If reader.ReadToDescendant("status") Then
                        Do
                            Dim message = ParseReply(reader)
                            ret.Add(message)
                        Loop While reader.ReadToFollowing("status")
                    End If
                End Using
            End Using
            Return ret
        End Function

        Public Function GetReplies(ByVal since As Integer) As List(Of FeedItem)
            Return GetRepliesImplementation(String.Format("http://www.twitter.com/replies.xml?since_id={0}", since))
        End Function

        Public Function GetReplies(ByVal since As DateTime) As List(Of FeedItem)
            Return GetRepliesImplementation(String.Format("http://www.twitter.com/replies.xml?since={0}", Uri.EscapeUriString(since.ToString(("ddd, dd MMM yyyy HH':'mm':'ss 'GMT'")))))
        End Function

        Public Function GetReplies() As List(Of FeedItem)
            Return GetRepliesImplementation("http://www.twitter.com/replies.xml")
        End Function

        Friend Shared Sub FindResponseErrors(ByVal response As WebResponse)
            If response.ResponseUri.AbsoluteUri = "http://twitter.com/400.html" Then
                Throw New TwitterException("Too many requests for the period of time")
            End If
            If response.ResponseUri.AbsoluteUri = "http://twitter.com/401.html" Then
                Throw New TwitterException("Invalid Credentials")
            End If
            If response.ResponseUri.AbsoluteUri = "http://twitter.com/403.html" Then
                Throw New TwitterException("Invalid request")
            End If
            If response.ResponseUri.AbsoluteUri = "http://twitter.com/404.html" Then
                Throw New TwitterException("Information not found")
            End If
            If response.ResponseUri.AbsoluteUri = "http://twitter.com/500.html" Then
                Throw New TwitterException("Internal server error")
            End If
            If response.ResponseUri.AbsoluteUri = "http://twitter.com/502.html" Then
                Throw New TwitterException("Twitter is down at the moment")
            End If
            If response.ResponseUri.AbsoluteUri = "http://twitter.com/503.html" Then
                Throw New TwitterException("Server too busy")
            End If
        End Sub

        Private Shared Function ParseReply(ByVal reader As XmlReader) As FeedItem
            Dim id = If(reader.ReadToFollowing("id"), Int64.Parse(reader.ReadElementContentAsString()), 0)
            Dim text = If(reader.ReadToFollowing("text"), reader.ReadElementContentAsString(), "")
            reader.ReadToFollowing("user")
            Dim user = If(reader.ReadToDescendant("name"), reader.ReadElementContentAsString(), "")
            Dim imageUrl = If(reader.ReadToFollowing("profile_image_url"), reader.ReadElementContentAsString(), "")
            'TODO: Use User class instead

            Dim message As New FeedItem(user, text, imageUrl, id)
            Return message
        End Function

        Private Shared Function ParseFeedItem(ByVal reader As XmlReader) As FeedItem
            Dim id As Long = 0
            If reader.ReadToFollowing("id") Then
                Dim sid = reader.ReadElementContentAsString()
                Int64.TryParse(sid.Substring(sid.IndexOf(":"c, 4) + 1), id)
            End If
            Dim text = If(reader.ReadToFollowing("title"), reader.ReadElementContentAsString(), "")
            Dim imageUrl = If(reader.ReadToFollowing("link") AndAlso reader.GetAttribute("rel") = "image", reader.GetAttribute("href").Replace("_normal.", "_bigger."), "")
            reader.ReadToFollowing("author")
            'TODO: Use User class instead
            Dim user = If(reader.ReadToDescendant("name"), reader.ReadElementContentAsString(), "")
            Dim userUri = If(reader.ReadToFollowing("uri"), reader.ReadElementContentAsString(), "")

            Dim message As New FeedItem(user, text, imageUrl, id) With {.UserUri = userUri}
            Return message
        End Function

        Private Function ParseUser(ByVal reader As XmlReader) As User
            Dim id = If(reader.ReadToFollowing("id"), reader.ReadElementContentAsInt(), 0)
            Dim name = If(reader.ReadToFollowing("name"), reader.ReadElementContentAsString(), "")
            Dim screenName = If(reader.ReadToFollowing("screen_name"), reader.ReadElementContentAsString(), "")
            Dim location = If(reader.ReadToFollowing("location"), reader.ReadElementContentAsString(), "")
            Dim description = If(reader.ReadToFollowing("description"), reader.ReadElementContentAsString(), "")

            Dim imageUrl = If(reader.ReadToFollowing("profile_image_url"), reader.ReadElementContentAsString(), "")

            Return New User(Me, id, name, screenName, location, description, imageUrl)
        End Function

        Public Sub New(ByVal credentials As ICredentials)
            connection = New Connection(credentials)
            'This is needed for compatibility with the twitter service.
            '.Net sends and Expect by default that is not supported by twitter.
            System.Net.ServicePointManager.Expect100Continue = False
        End Sub

        Public Sub New(ByVal username As String, ByVal password As String)
            Me.New(New NetworkCredential(username, password))
        End Sub

        Public ReadOnly Property Followers As List(Of User)
            Get
                Dim ret As New List(Of User)
                Dim url = "http://www.twitter.com/statuses/followers/{0}.xml"

                Dim request = connection.CreateRequestWithUserName(url)
                Using response = request.GetResponse()
                    FindResponseErrors(response)
                    Using reader = XmlReader.Create(response.GetResponseStream())
                        If reader.ReadToDescendant("user") Then
                            Do
                                Dim user = ParseUser(reader)
                                ret.Add(user)
                            Loop While reader.ReadToFollowing("user")
                        End If
                    End Using
                End Using
                Return ret
            End Get
        End Property

        Public ReadOnly Property Friends As List(Of User)
            Get
                Dim ret As New List(Of User)
                Dim url = "http://www.twitter.com/statuses/friends/{0}.xml"

                Dim request = connection.CreateRequestWithUserName(url)
                Using response = request.GetResponse()
                    FindResponseErrors(response)
                    Using reader = XmlReader.Create(response.GetResponseStream())
                        If reader.ReadToDescendant("user") Then
                            Do
                                Dim user = ParseUser(reader)
                                ret.Add(user)
                            Loop While reader.ReadToFollowing("user")
                        End If
                    End Using
                End Using
                Return ret
            End Get
        End Property

        Friend Sub UpdateStatus(ByVal message As String)
            Dim myUri = "http://www.twitter.com/statuses/update.xml"
            PostToTwitter(myUri, String.Concat("status=", Uri.EscapeDataString(message)))
        End Sub

        Friend Sub SendMessage(ByVal name As String, ByVal message As String)
            Dim myUri = "http://www.twitter.com/statuses/update.xml"
            PostToTwitter(myUri, String.Concat("status=", Uri.EscapeDataString(String.Format("@{0} {1}", name, message)), "&in_reply_to_status_id="))
        End Sub

        Friend Sub SendPrivateMessage(ByVal name As String, ByVal message As String)
            Dim myUri = "http://www.twitter.com/direct_messages/new.xml"
            PostToTwitter(myUri, String.Concat("user=", name, "&text=", Uri.EscapeDataString(message)))
        End Sub

        Private Function PostToTwitter(ByVal uri As String, ByVal message As String) As String
            Dim request = connection.CreateRequest(uri)
            request.Method = "POST"
            Dim data() = Encoding.UTF8.GetBytes(message)
            request.ContentType = "application/x-www-form-urlencoded"
            Dim http = TryCast(request, HttpWebRequest)
            If http IsNot Nothing Then
                http.UserAgent = "Visual Studio"
            End If
            request.ContentLength = data.Length

            ' Submit the data.
            Using requestStream = request.GetRequestStream()
                requestStream.Write(data, 0, data.Length)

                Using response = request.GetResponse()
                    FindResponseErrors(response)
                    Using responseStream = response.GetResponseStream()
                        ' throw if an error happened.
                        Return New StreamReader(responseStream).ReadToEnd()
                    End Using
                End Using
            End Using
        End Function
    End Class
End Namespace
